# -*- coding: utf-8 -*-
# @Time         : 2021/5/8 11:10
# @Author       : Jinxing Lin
# @StudentNumber: 20216523
# @Affiliation  : SUN YAT-SEN UNIVERSITY  SCHOOL OF SYSTEMS SCIENCE AND ENGINEERING
# @Mail         ：linjx83@mail2.sysu.edu.cn
# @FileName     : utiils_perf.py
# @Software     : PyCharm

import pandas as pd
import numpy as np
import time
from sklearn.metrics import accuracy_score
import os


def exeTime(func):
    '''
        计算时间修饰器
    :param func:
    :return:
    '''

    def newFunc(*args, **args2):
        t0 = time.time()
        back = func(*args, **args2)
        t1 = time.time()
        print(t1 - t0)
        return back

    return newFunc


def perf_measure(y_true, y_pred):
    '''
        由真实标签和预测标签输出TP/FP/TN/FN（仅针对二分类，且正样本标签值为1）
    :param y_true: 真实标签
    :param y_pred: 预测标签
    :return:
    '''

    TP = 0
    FP = 0
    TN = 0
    FN = 0

    for i in range(len(y_pred)):
        if y_true[i] == y_pred[i] and y_pred[i] == 1:
            TP += 1
        elif y_pred[i] == 1 and y_true[i] == 0:
            FP += 1
        elif y_pred[i] == 0 and y_true[i] == 0:
            TN += 1
        elif y_pred[i] == 0 and y_true[i] == 1:
            FN += 1

    TPR = TP / (TP + FN)  # /recall查全率
    FPR = FP / (FP + TN)
    TNR = TN / (FP + TN)
    FNR = FN / (TP + FN)
    precision = TP / (TP + FP)  # 查准率
    f1 = (2 * precision * TPR) / (precision + TPR)  # 范围：0-1

    return TPR, \
           FPR, \
           TNR, \
           FNR, \
           precision, \
           f1


def func_cls_frame(data_train, data_test, label_train, label_test, cls_func):
    '''
        使用SVM进行分类
    :param data_train:
    :param label_train:
    :param data_test:
    :param label_test:
    :return:
    '''
    time_start = time.time()  # time start
    pred_test = cls_func(data_train, data_test, label_train)
    precision_all = accuracy_score(y_true=label_test, y_pred=pred_test)
    time_end = time.time()  # time end

    # TP/FP/FN/TN
    # 尽可能让TP和FN高
    # 0为正样本，1为负样本

    TPR, FPR, TNR, FNR, precision, f1 = perf_measure(label_test, pred_test)
    # print("TPR/recall:", TPR)
    # print("FPR:", FPR)
    # print("TNR:", TNR)
    # print("FNR:", FNR)
    # print("precision of label 1:", precision)
    # print("f1 score:", f1)

    # save
    # df_res = pd.DataFrame({
    #     'true': label_test,
    #     'pred': pred_test
    # })
    # df_res.to_csv("res1_cv.csv", index=False)

    return precision_all, TPR, FPR, TNR, FNR, precision, f1, time_end - time_start


def loop_cls(ds, func_cls, loop_num=100):
    '''
        循环loop_num次
    :param ds: 数据集对象
    :param loop_num: 循环次数
    :return:
    '''
    mPrecision_all = 0
    mTPR = 0
    mFPR = 0
    mTNR = 0
    mFNR = 0
    mPrecision = 0
    mF1 = 0
    mTime = 0
    for i in range(loop_num):
        data_train, data_test, label_train, label_test = ds.split_train_test()
        # 样本极度不均衡，被模型全部预测成相同的值
        # print(1-sum(label_test)/len(label_test))

        Precision_all, TPR, FPR, TNR, FNR, Precision, F1, Time = \
            func_cls_frame(data_train, data_test, label_train, label_test, func_cls)
        mPrecision_all += Precision_all
        mTPR += TPR
        mFPR += FPR
        mTNR += TNR
        mFNR += FNR
        mPrecision += Precision
        mF1 += F1
        mTime += Time

    # print mean
    print("mean of Precision_all:", mPrecision_all / loop_num)
    print("mean of TPR/recall:", mTPR / loop_num)
    print("mean of FPR:", mFPR / loop_num)
    print("mean of TNR:", mTNR / loop_num)
    print("mean of FNR:", mFNR / loop_num)
    print("mean of precision of label 1:", mPrecision / loop_num)
    print("mean of f1 score:", mF1 / loop_num)
    print("mean of Time", mTime / loop_num)

    # save as csv
    mean_data = np.asarray([mPrecision_all / loop_num, mTPR / loop_num, mFPR / loop_num, mTNR / loop_num,
                            mFNR / loop_num, mPrecision / loop_num, mF1 / loop_num, mTime / loop_num])
    mean_data = mean_data.reshape((-1, 1))
    names = ["准确度",
             "真阳性率/召回率",
             "假阳性率",
             "真阴性率",
             "假阴性率",
             "查准率",
             "F1分数",
             "时间"
             ]
    mean_data = pd.DataFrame(mean_data)
    mean_data.index = names
    mean_data.columns = [ds.test_proportion]

    # create dir
    if not os.path.exists("../result/result_{}".format(func_cls.__name__)):
        os.mkdir("../result/result_{}".format(func_cls.__name__))

    mean_data.to_csv("../result/result_{}/{}.csv".format(func_cls.__name__,
                                               int(ds.test_proportion * 100)),
                     index=True,
                     header=True,
                     encoding="utf_8_sig")
